package net.xiaohanlin.smartmetric.monitor.system;

import java.util.HashMap;
import java.util.Map;

import org.hyperic.sigar.Mem;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.Swap;

import com.codahale.metrics.Gauge;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricSet;

import net.xiaohanlin.smartmetric.metricname.MemoryMetricNameEnum;

public class MemoryGaugeSet extends AbstractSystemMetric implements MetricSet {

	@Override
	public Map<String, Metric> getMetrics() {
		Map<String, Metric> gauges = new HashMap<>(MemoryMetricNameEnum.values().length);
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_free),  new Gauge<Long>() {
			public Long getValue() {
				return mem().free();
			}
		});
		
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_actual_free),  new Gauge<Long>() {
			public Long getValue() {
				return mem().actualFree();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_used),  new Gauge<Long>() {
			public Long getValue() {
				return mem().used();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_actual_used),  new Gauge<Long>() {
			public Long getValue() {
				return mem().actualUsed();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_total),  new Gauge<Long>() {
			public Long getValue() {
				return mem().total();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_used_percent),  new Gauge<Double>() {
			public Double getValue() {
				return mem().usedPercent();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.memory_free_percent),  new Gauge<Double>() {
			public Double getValue() {
				return mem().freePercent();
			}
		});
		
		gauges.put(getMetricName(MemoryMetricNameEnum.swap_free),  new Gauge<Long>() {
			public Long getValue() {
				return swap().free();
			}
		});
		gauges.put(getMetricName(MemoryMetricNameEnum.swap_pages_in),  new Gauge<Long>() {
			public Long getValue() {
				return swap().pagesIn();
			}
		});
		gauges.put(getMetricName(MemoryMetricNameEnum.swap_pages_out),  new Gauge<Long>() {
			public Long getValue() {
				return swap().pagesOut();
			}
		});
		return gauges;
	}

	public MainMemory mem() {
		try {
			return MainMemory.fromSigarBean(sigar.getMem());
		} catch (SigarException e) {
			return MainMemory.undef();
		}
	}

	public SwapSpace swap() {
		try {
			return SwapSpace.fromSigarBean(sigar.getSwap());
		} catch (SigarException e) {
			return SwapSpace.undef();
		}
	}

	public long ramInMB() {
		try {
			return sigar.getMem().getRam();
		} catch (SigarException e) {
			return -1L;
		}
	}
	
	public static abstract class MemSegment {

		protected final long total;
		protected final long used;
		protected final long free;

		private MemSegment(long total, long used, long free) {
			this.total = total;
			this.used = used;
			this.free = free;
		}

		public long total() {
			return total;
		}

		public long used() {
			return used;
		}

		public long free() {
			return free;
		}
	}

	public static final class MainMemory extends MemSegment {

		private final long actualUsed, actualFree;
		private final double usedPercent, freePercent;

		private MainMemory(//
				long total, long used, long free, //
				long actualUsed, long actualFree, double usedPercent, double freePercent) {
			super(total, used, free);
			this.actualUsed = actualUsed;
			this.actualFree = actualFree;
			this.usedPercent = usedPercent;
			this.freePercent = freePercent;
		}

		public static MainMemory fromSigarBean(Mem mem) {
			return new MainMemory( //
					mem.getTotal(), // 内存总量
					mem.getUsed(), // 当前内存使用量
					mem.getFree(), // 当前内存剩余量
					mem.getActualUsed(), // 实际使用量
					mem.getActualFree(), // 实际剩余量
					mem.getUsedPercent(), //// 使用率
					mem.getFreePercent());// 剩余率
		}

		private static MainMemory undef() {
			return new MainMemory(-1L, -1L, -1L, -1L, -1L, -1, -1);
		}

		public long actualUsed() {
			return actualUsed;
		}

		public long actualFree() {
			return actualFree;
		}

		public double usedPercent() {
			return usedPercent;
		}

		public double freePercent() {
			return freePercent;
		}
	}

	public static final class SwapSpace extends MemSegment {

		private final long pagesIn, pagesOut;

		private SwapSpace( //
				long total, long used, long free, //
				long pagesIn, long pagesOut) {
			super(total, used, free);
			this.pagesIn = pagesIn;
			this.pagesOut = pagesOut;
		}

		public static SwapSpace fromSigarBean(Swap swap) {
			return new SwapSpace( //
					swap.getTotal(), swap.getUsed(), swap.getFree(), //
					swap.getPageIn(), swap.getPageOut());
		}

		private static SwapSpace undef() {
			return new SwapSpace(-1L, -1L, -1L, -1L, -1L);
		}

		public long pagesIn() {
			return pagesIn;
		}

		public long pagesOut() {
			return pagesOut;
		}
	}
}
